{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%pylab inline\n",
    "from sympy import init_printing\n",
    "init_printing(use_latex='mathjax')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Camera Calibration\n",
    "\n",
    "详细信息可见[doc](http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html#calibration)\n",
    "\n",
    "### 镜像畸变矫正 \n",
    "\n",
    "<img src=\"data/RadialDistortionCorrectedFormular.png\" alt=\"Radial Distortion Correct\"/>\n",
    "\n",
    "### 切向畸变矫正\n",
    "\n",
    "<img src=\"data/TangentialDistortionCorrectedFormular.png\" alt=\"Tangential Distortion Correct\"/>\n",
    "\n",
    "### 畸变参数排列形式\n",
    "\n",
    "<img src=\"data/DistortionCoefficients.png\" alt=\"Distortion Coefficients\"/>\n",
    "\n",
    "### 相机矩阵\n",
    "\n",
    "<img src=\"data/CameraMatrix.png\" alt=\"Camera Matrix\"/>\n",
    "\n",
    "\n",
    "### 相关的 opencv 函数\n",
    "\n",
    "* [cv2.findChessboardCorners()](http://docs.opencv.org/2.4.8/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#findchessboardcorners) 矩形标定板的角点\n",
    "* [cv2.findCirclesGrid()](http://docs.opencv.org/2.4.8/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#findcirclesgrid) 圆形标定板用\n",
    "* [cv2.cornerSubPix()](http://docs.opencv.org/2.4.8/modules/imgproc/doc/feature_detection.html#cornersubpix) 寻找亚像素级别，提高精度\n",
    "* [cv2.drawChessboardCorners()](http://docs.opencv.org/2.4.8/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#drawchessboardcorners) 绘制标定板角点\n",
    "\n",
    "### 标定函数\n",
    "* [cv2.calibrateCamera()](http://docs.opencv.org/2.4.8/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#calibratecamera)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import os\n",
    "import glob\n",
    "\n",
    "w, h = 12, 9 # 标定板大小\n",
    "\n",
    "# termination criteria 迭代终止标准\n",
    "criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)\n",
    "\n",
    "# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)\n",
    "# 准备目标点，三维空间中的，以棋盘格的为单位长度\n",
    "objp = np.zeros((h*w,3), np.float32)\n",
    "objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)\n",
    "\n",
    "# Arrays to store object points and image points from all the images.\n",
    "# 从所有图像中储存所有的目标点和图像点\n",
    "objpoints = [] # 3d point in real world space\n",
    "imgpoints = [] # 2d points in image plane.\n",
    "\n",
    "images = glob.glob(os.path.join('data','img','img_*.jpg'))\n",
    "\n",
    "for fname in images:\n",
    "    img = cv2.imread(fname)\n",
    "    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "    # Find the chess board corners\n",
    "    # 寻找标定角点\n",
    "    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)\n",
    "\n",
    "    # If found, add object points, image points (after refining them)\n",
    "    # 如果找到角点，添加目标点和图像点\n",
    "    if ret == True:\n",
    "        objpoints.append(objp)\n",
    "        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) # 经试验发现 cornerSubPix 就地改变了 corners\n",
    "        imgpoints.append(corners)\n",
    "        # Draw and display the corners\n",
    "        # 绘制并显示角点\n",
    "        #img = cv2.drawChessboardCorners(img, (w,h), corners,ret)\n",
    "        cv2.drawChessboardCorners(img, (w,h), corners,ret) # 经试验发现 drawChessboardCorners 就地改变了img\n",
    "        cv2.imshow('img',img)\n",
    "        cv2.waitKey(500)\n",
    "        \n",
    "\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "mtx.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "dist.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "ret # the final re-projection error"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 相机矩阵\n",
    "fx,fy,cx,cy = mtx[0,0],mtx[1,1],mtx[0,2],mtx[1,2]\n",
    "mtx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 矫正参数\n",
    "k1,k2,p1,p2,k3 = dist[0]\n",
    "dist # 矫正参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 旋转向量\n",
    "img1_rvec = rvecs[0] # 第一幅标定板的旋转向量\n",
    "img1_rvec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 平移向量\n",
    "img1_tvec = tvecs[0] # 第一幅标定板的平移向量\n",
    "img1_tvec"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 消除畸变 undistortion\n",
    "\n",
    "### 使用 cv2.undistort\n",
    "* [cv2.undistort()](http://docs.opencv.org/2.4.8/modules/imgproc/doc/geometric_transformations.html#undistort) 转换图像以补偿镜头失真"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "img = cv2.imread(images[0])\n",
    "h,w = img.shape[:2]\n",
    "imshow(img[:,:,::-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "src = img\n",
    "cameraMatrix = mtx\n",
    "distCoeffs = dist\n",
    "dst = cv2.undistort(src,cameraMatrix,distCoeffs)\n",
    "imshow(dst[:,:,::-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用重映射\n",
    "* [cv2.getOptimalNewCameraMatrix()](http://docs.opencv.org/2.4.8/modules/imgproc/doc/geometric_transformations.html)\n",
    "* [cv2.initUndistortRectifyMap()](http://docs.opencv.org/2.4.8/modules/imgproc/doc/geometric_transformations.html)\n",
    "* [cv2.remap()](http://docs.opencv.org/2.4.8/modules/imgproc/doc/geometric_transformations.html)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# undistort\n",
    "h,  w = img.shape[:2]\n",
    "cameraMatrix = mtx\n",
    "distCoeffs = dist\n",
    "imageSize = (w,h)\n",
    "alpha = 1 # 0 (当未失真图像中的所有像素都有效时) 1 (当所有源图像像素保留在未失真图像中时)\n",
    "newImgSize = imageSize\n",
    "newcameramtx, roi=cv2.getOptimalNewCameraMatrix(cameraMatrix,distCoeffs,imageSize,alpha,newImgSize) \n",
    "\n",
    "R = None # stereoRectify() 产生的用于立体视觉\n",
    "m1type = cv2.CV_32FC1 # Type of the first output map that can be CV_32FC1 or CV_16SC2\n",
    "mapx,mapy = cv2.initUndistortRectifyMap(cameraMatrix,distCoeffs,R,newcameramtx,imageSize,m1type)\n",
    "interpolation = cv2.INTER_LINEAR # 差值方法\n",
    "dst = cv2.remap(img,mapx,mapy,interpolation)\n",
    "\n",
    "# crop the image\n",
    "x,y,w,h = roi\n",
    "dst = dst[y:y+h, x:x+w]\n",
    "imshow(dst[:,:,::-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "np.eye(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
